using System;
using Moq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class PrecedenceTermSwapperSpecification {
        [Test]
        public void ShouldSwapIfOuterIsHigherPrecedenceThanInner () {
            var precedences = CreatePrecedences ();
            var outer = new InfixCompositeTerm ("outer", precedences.Higher, "first", "last", null);
            var one = new IntegerTerm (1, null);
            outer.First = one;

            var inner = new InfixCompositeTerm ("inner", precedences.Lower, "first", "last", null);
            var two = new IntegerTerm (2, null);
            inner.First = two;
            var three = new IntegerTerm (3, null);
            inner.Last = three;

            outer.Last = inner;

            var swapper = new PrecedenceTermSwapper ();
            var newOuter = (InfixCompositeTerm) swapper.Swap (outer);

            Assert.That (newOuter, Is.SameAs (inner));
            Assert.That (newOuter.First, Is.SameAs (outer));
            Assert.That (newOuter.Last, Is.SameAs (three));

            var newInner = (InfixCompositeTerm) newOuter.First;
            Assert.That (newInner.First, Is.SameAs (one));
            Assert.That (newInner.Last, Is.SameAs (two));
        }

        [Test]
        public void ShouldNotSwapIfOuterIsLowerPrecedenceThanInner () {
            var precedences = CreatePrecedences ();
            var outer = new InfixCompositeTerm ("outer", precedences.Lower, "first", "last", null);
            var one = new IntegerTerm (1, null);
            outer.First = one;

            var inner = new InfixCompositeTerm ("inner", precedences.Higher, "first", "last", null);
            var two = new IntegerTerm (2, null);
            inner.First = two;
            var three = new IntegerTerm (3, null);
            inner.Last = three;

            outer.Last = inner;

            var swapper = new PrecedenceTermSwapper ();
            var newOuter = (InfixCompositeTerm) swapper.Swap (outer);

            Assert.That (newOuter, Is.SameAs (outer));
            Assert.That (newOuter.First, Is.SameAs (one));
            Assert.That (newOuter.Last, Is.SameAs (inner));

            var newInner = (InfixCompositeTerm) newOuter.Last;
            Assert.That (newInner.First, Is.SameAs (two));
            Assert.That (newInner.Last, Is.SameAs (three));
        }

        class Precedences {
            public IPrecedence Lower, Higher;
        }

        Precedences CreatePrecedences () {
            var higher = new Mock<IPrecedence> ();
            var lower = new Mock<IPrecedence> ();

            higher.Setup (p => p.IsGreaterThan (lower.Object)).Returns (true);
            lower.Setup (p => p.IsGreaterThan (higher.Object)).Returns (false);

            return new Precedences () {Higher = higher.Object, Lower = lower.Object};
        }
    }
}